import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;

public class Gobang extends JFrame implements java.io.Serializable {
    //定义一个boolean量判断文件是否打开成功
    boolean victory = true;
    //定义一个Boolean量判断哪方获胜
    boolean winFlag;
    //定义当前所下棋子的位置
    int xPos, yPos, lastxPos, lastyPos;
    //定义三个BufferedImage，分别代表棋盘图、黑子图、白子图
    private BufferedImage table;
    private BufferedImage black;
    private BufferedImage white;

    //定义一个BufferedImage，代表当鼠标移动时将要下子的选择框
    private BufferedImage selected;

    //定义棋盘的宽高，这里的定义尺寸和给定的board.jpg图片的尺寸一致因为棋盘背景是通过图片加载的
    private final int TABLE_WIDTH = 535;
    private final int TABLE_HEIGHT = 536;

    //定义棋盘中，每行和每列可下子的数目，这个数目跟给定的board.jpg中的数目是一致的，都为15
    private final int BOARD_SIZE = 15;

    //定义每个棋子所占棋盘总宽度的大小比值；每个棋子所占宽度 535/15=35
    private final int RATE = TABLE_WIDTH / BOARD_SIZE;

    //定义棋盘有效区域与背景图坐标之间的偏移值，x坐标右移5个像素，y坐标下移6个像素
    private final int X_OFFSET = 5;
    private final int Y_OFFSET = 6;
    /*

        定义一个二维数组充当棋盘上每个位置处的棋子；
        该数组的索引与该棋子在棋盘上的坐标需要有一个对应关系：
            例如： 索引[2][3]处的棋子，对一个的真实绘制坐标应该是：
               xpos = 2*RATE+X_OFFSET=75;
                ypos = 3*RATE+Y_OFFSET=111;

     */
    private int[][] board = new int[BOARD_SIZE][BOARD_SIZE];
    //如果存储0，代表没有棋子，如果存储1，代表黑棋，如果存储2，代表白棋

    //定义一个Boolean量判断当前是黑子还是白子
    boolean isBlack = true;
    //定义一个Boolean量判断是否悔棋
    boolean reback = false;

    //定义五子棋游戏棋盘对应的Canvas组件
    private class ChessBoard extends JPanel {
        //重写paint方法，实现绘画
        @Override
        public void paint(Graphics g) {
            //绘制五子棋棋盘
            g.drawImage(table, 0, 0, null);
            //绘制选中点的红框
            if (selectX > 0 && selectY > 0) {
                g.drawImage(selected, selectX * RATE + X_OFFSET, selectY * RATE + Y_OFFSET, null);
            }

            //遍历数组，绘制棋子
            for (int i = 0; i < BOARD_SIZE; i++) {
                for (int j = 0; j < BOARD_SIZE; j++) {
                    //绘制黑棋
                    if (board[i][j] == 1) {
                        g.drawImage(black, i * RATE + X_OFFSET, j * RATE + Y_OFFSET, null);
                    }
                    //绘制白棋
                    if (board[i][j] == 2) {
                        g.drawImage(white, i * RATE + X_OFFSET, j * RATE + Y_OFFSET, null);
                    }
                }
            }
        }
    }

    private ChessBoard chessBoard = new ChessBoard();

    //定义变量，记录当前选中的坐标点对应的board数组中对应的棋子索引；
    private int selectX = -1;
    private int selectY = -1;

    //定义Panel,放置点击按钮
    JPanel p = new JPanel();
    JButton clearBtn = new JButton("悔棋");
    JButton save = new JButton("保存");
    JButton flesh = new JButton("重置");
    JButton defate = new JButton("认输");


    public void BtnColor(Color clearBtnColor, Color saveColor, Color fleshColor, Color defateColor) {
        clearBtn.setBackground(clearBtnColor);
        save.setBackground(saveColor);
        flesh.setBackground(fleshColor);
        defate.setBackground(defateColor);
    }

    //初始化棋盘，清除所有棋子
    public void firstcomposs() {
        for (int i = 0; i < BOARD_SIZE; i++) {
            for (int j = 0; j < BOARD_SIZE; j++) {
                board[i][j] = 0;
            }
        }

    }

    //保存的方法
    public void saveFile(int[][] chess) {
        String fileName = "random.int";
        try {
            OutputStream fos = new FileOutputStream(fileName);
            DataOutputStream dos = new DataOutputStream(fos);
            for (int i = 0; i < chess.length; i++) {
                for (int j = 0; j < chess[i].length; j++) {
                    dos.writeInt(chess[i][j]);
                }
                //强制写入并关闭
                // dos.flush();
                dos.close();
            }
        } catch (FileNotFoundException ex) {

        } catch (IOException ex) {

        }
    }

    //打开文件
    public int[][] openFile() {
        int ch[][] = new int[BOARD_SIZE][BOARD_SIZE];
        String fileName = "random.int";
        try {
            InputStream fis = new FileInputStream(fileName);
            DataInputStream dis = new DataInputStream(fis);
            for (int i = 0; i < ch.length; i++) {
                for (int j = 0; j < ch[i].length; j++) {
                    ch[i][j] = dis.readInt();
                }
            }
            dis.close();
        }/* catch (Exception e) {
            firstcomposs();
            e.printStackTrace();
        }*/ catch (FileNotFoundException ex) {
            firstcomposs();
        } catch (IOException ex) {

        }
        return ch;
    }

    public void init() throws Exception {

        //初始化按钮的颜色
        BtnColor(Color.LIGHT_GRAY, Color.LIGHT_GRAY, Color.LIGHT_GRAY, Color.LIGHT_GRAY);
        ActionListener MyListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                switch (e.getActionCommand()) {
                    case "悔棋":
                        BtnColor(Color.yellow, Color.LIGHT_GRAY, Color.LIGHT_GRAY, Color.LIGHT_GRAY);
                        reback = true;
                        break;
                    case "保存":
                        BtnColor(Color.LIGHT_GRAY, Color.yellow, Color.LIGHT_GRAY, Color.LIGHT_GRAY);
                        saveFile(board);
                        break;
                    case "认输":
                        BtnColor(Color.LIGHT_GRAY, Color.LIGHT_GRAY, Color.lightGray, Color.yellow);
                        JOptionPane.showMessageDialog(p, "游戏结束，" + (board[xPos][yPos] == 1 ? "黑方" : "白方") + "获胜");
                        firstcomposs();
                        chessBoard.repaint();
                        break;
                    case "重置":
                        BtnColor(Color.LIGHT_GRAY, Color.LIGHT_GRAY, Color.yellow, Color.LIGHT_GRAY);
                        firstcomposs();
                        chessBoard.repaint();
                        break;
                }
            }
        };

        clearBtn.addActionListener(MyListener);
        save.addActionListener(MyListener);
        flesh.addActionListener(MyListener);
        defate.addActionListener(MyListener);

        p.add(clearBtn);
        p.add(save);
        p.add(flesh);
        p.add(defate);
        //把Panel放入到窗口底部
        this.add(p, BorderLayout.EAST);

        try {
            //初始化黑棋，白棋，棋盘,选中框
            table = ImageIO.read(new File("D:\\Learning\\Java\\代码\\awt编程代码\\awt\\img\\board.jpg"));
            black = ImageIO.read(new File("D:\\Learning\\Java\\代码\\awt编程代码\\awt\\img\\black.gif"));
            white = ImageIO.read(new File("D:\\Learning\\Java\\代码\\awt编程代码\\awt\\img\\white.gif"));
            selected = ImageIO.read(new File("D:\\Learning\\Java\\代码\\awt编程代码\\awt\\img\\selected.gif"));
        } catch (IOException ex) {
            JOptionPane.showMessageDialog(p, "文件打开失败,请检查文件路径!");
            victory = false;

        }

        //初始化board数组，默认情况下，所有位置处都没有棋子
        //firstcomposs();
        board = openFile();

        //设置chessBoard的最佳大小
        chessBoard.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));

        //给chessBoard注册鼠标监听器
        chessBoard.addMouseListener(new MouseAdapter() {
            //鼠标单击会触发
            @Override
            public void mouseClicked(MouseEvent e) {
                //将用户鼠标的坐标，转换成棋子的坐标
                xPos = (e.getX() - X_OFFSET) / RATE;
                yPos = (e.getY() - Y_OFFSET) / RATE;
                //判断当前要下什么颜色的棋子
                if (board[xPos][yPos] == 0) {
                    board[xPos][yPos] = 1;
                    if (isBlack) {
                        board[xPos][yPos] = 1;
                        isBlack = false;
                    } else {
                        board[xPos][yPos] = 2;
                        isBlack = true;
                    }
                   // lastxPos = xPos;
                 //  lastyPos = yPos;
                } else {
                    JOptionPane.showMessageDialog(p, "该处已有棋子，请在别处落子！");
                }
               /* if (reback) {
                    board[lastxPos][lastyPos] = 0;
                    reback = false;
                }*/
                winFlag = isWin();
                if (winFlag == true) {
                    JOptionPane.showMessageDialog(p, "游戏结束，" + (board[xPos][yPos] == 1 ? "黑方" : "白方") + "获胜");
                    firstcomposs();
                }
                //重绘chessBoard
                chessBoard.repaint();
            }

            //当鼠标退出棋盘区域后，复位选中坐标,重绘chessBoard，要保证红色选中框显示正确
            @Override
            public void mouseExited(MouseEvent e) {
                selectX = -1;
                selectY = -1;
                chessBoard.repaint();
            }
        });

        //给chessBoard注册鼠标移动监听器
        chessBoard.addMouseMotionListener(new MouseMotionAdapter() {
            //当鼠标移动时，修正selectX和selectY，重绘chessBoard，要保证红色选中框显示正确
            @Override
            public void mouseMoved(MouseEvent e) {
                //将鼠标的坐标，转换成棋子的索引
                selectX = (e.getX() - X_OFFSET) / RATE;
                selectY = (e.getY() - Y_OFFSET) / RATE;
                chessBoard.repaint();
            }
        });
//把chessBoard添加到Frame中
        this.add(chessBoard);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        this.pack();
        this.setVisible(victory);
    }

    //定义五子棋游戏窗口
    public Gobang() {
        super("五子棋");
        this.setResizable(false);

    }

    //判断哪方赢
    public boolean isWin() throws ArrayIndexOutOfBoundsException {
        boolean coutinue = false;
        int continueCount = 1; //连续相同棋子的个数 xpos ypos 当前所下棋子的坐标
        int count = 1;
        // while (xPos - count >= 0 && xPos + count <= 14 && yPos + count >= 0 && yPos + count <= 14) {
        //横向
        while (board[xPos][yPos] == board[xPos + count][yPos]) {
            continueCount++;
            count++;

        }
        count = 1;
        while (board[xPos][yPos] == board[xPos - count][yPos]) {
            continueCount++;
            count++;
        }
       if (continueCount >= 5)
            coutinue = true;
        //纵向
        continueCount = 1;
        count = 1;
        while (board[xPos][yPos] == board[xPos][yPos + count]) {
            continueCount++;
            count++;
        }
        count = 1;
        while (board[xPos][yPos] == board[xPos][yPos - count]) {
            continueCount++;
            count++;
        }
        if (continueCount >= 5)
            coutinue = true;
        //正斜向
        continueCount = 1;
        count = 1;
        while (board[xPos][yPos] == board[xPos + count][yPos - count]) {
            continueCount++;
            count++;
        }
        count = 1;
        while (board[xPos][yPos] == board[xPos - count][yPos + count]) {
            continueCount++;
            count++;
        }
        if (continueCount >= 5)
            coutinue = true;
        //反斜向
        continueCount = 1;
        count = 1;
        while (board[xPos][yPos] == board[xPos + count][yPos + count]) {
            continueCount++;
            count++;
        }
        count = 1;
        while (board[xPos][yPos] == board[xPos - count][yPos - count]) {
            continueCount++;
            count++;
        }
        if (continueCount >= 5)
            coutinue = true;
        return coutinue;
    }


    public static void main(String[] args) throws Exception {
        new Gobang().init();
    }
}

  /*  判断哪方赢  判断当前下的棋子与周围的有没有相同颜色
    private boolean isWin(){
        int continueCount=1;//连续棋子的个数
        for(int x=xIndex-1;x>=0;x--){//横向向左寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(x,yIndex,c)!=null){
                continueCount++;
            }else
                break;
        }
        for(int x=xIndex+1;x<=ROWS;x++){//横向向右寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(x,yIndex,c)!=null){
                continueCount++;
            }else
                break;
        }
        if(continueCount>=5){//判断记录数大于等于五，即表示此方获胜
            return true;
        }else
            continueCount=1;
        //
        for(int y=yIndex-1;y>=0;y--){//纵向向上寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(xIndex,y,c)!=null){
                continueCount++;
            }else
                break;
        }
        for(int y=yIndex+1;y<=ROWS;y++){//纵向向下寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(xIndex,y,c)!=null){
                continueCount++;
            }else
                break;
        }
        if(continueCount>=5){//判断记录数大于等于五，即表示此方获胜
            return true;
        }else
            continueCount=1;
        //
        for(int x=xIndex+1,y=yIndex-1;y>=0&&x<=COLS;x++,y--){//右下寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(x,y,c)!=null){
                continueCount++;
            }else
                break;
        }
        for(int x=xIndex-1,y=yIndex+1;y<=ROWS&&x>=0;x--,y++){//左上寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(x,y,c)!=null){
                continueCount++;
            }else
                break;
        }
        if(continueCount>=5){//判断记录数大于等于五，即表示此方获胜
            return true;
        }else
            continueCount=1;
        //
        for(int x=xIndex-1,y=yIndex-1;y>=0&&x>=0;x--,y--){//左下寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(x,y,c)!=null){
                continueCount++;
            }else
                break;
        }
        for(int x=xIndex+1,y=yIndex+1;y<=ROWS&&x<=COLS;x++,y++){//右上寻找
            Color c=isBack ? Color.black : Color.white;
            if(getChess(x,y,c)!=null){
                continueCount++;
            }else
                break;
        }
        if(continueCount>=5){//判断记录数大于等于五，即表示此方获胜
            return true;
        }else
            continueCount=1;
        return false;
    }
}*/
